package com.yf.exam.modules.exam.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.base.api.api.dto.PagingReqDTO;
import com.yf.base.api.exception.ServiceException;
import com.yf.base.enums.OpenType;
import com.yf.base.utils.BeanMapper;
import com.yf.base.utils.CronUtils;
import com.yf.book.job.UpdateBookJob;
import com.yf.book.job.dto.AddBookDTO;
import com.yf.book.job.enums.BookType;
import com.yf.course.modules.course.dto.CourseDTO;
import com.yf.course.modules.course.entity.Course;
import com.yf.course.modules.course.entity.CourseDepart;
import com.yf.exam.job.ExamNotifyJob;
import com.yf.exam.modules.exam.dto.ExamDTO;
import com.yf.exam.modules.exam.dto.OpenAIExamDTO;
import com.yf.exam.modules.exam.dto.request.ExamNotifyReqDTO;
import com.yf.exam.modules.exam.dto.request.ExamReqDTO;
import com.yf.exam.modules.exam.dto.request.ExamSaveReqDTO;
import com.yf.exam.modules.exam.dto.response.ExamCheckRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamOnlineRespDTO;
import com.yf.exam.modules.exam.dto.response.ExamReviewRespDTO;
import com.yf.exam.modules.exam.entity.Exam;
import com.yf.exam.modules.exam.entity.ExamDepart;
import com.yf.exam.modules.exam.mapper.ExamMapper;
import com.yf.exam.modules.exam.service.*;
import com.yf.exam.modules.paper.service.PaperService;
import com.yf.exam.modules.tmpl.entity.Tmpl;
import com.yf.exam.modules.tmpl.service.TmplService;
import com.yf.job.service.JobService;
import com.yf.system.modules.user.UserUtils;
import com.yf.system.modules.user.entity.SysUser;
import com.yf.system.modules.user.service.SysUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
* <p>
* 考试业务实现类
* </p>
*
* @author 聪明笨狗
* @since 2020-07-25 16:18
*/
@Service
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {

    @Autowired
    private ExamDepartService examDepartService;

    @Autowired
    private ExamPersonService examPersonService;

    @Autowired
    private TmplService tmplService;

    @Autowired
    private PaperService paperService;

    @Autowired
    private ExamApplyService examApplyService;


    @Autowired
    private ExamRecordService examRecordService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private JobService jobService;



    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(ExamSaveReqDTO reqDTO) {

        // ID
        String tempId = reqDTO.getId();
        String prodId = reqDTO.getId();

        if(tempId.startsWith("temp_")){
            prodId = IdWorker.getIdStr();
        }

        //复制参数
        Exam entity = new Exam();

        // 复制基本数据
        BeanMapper.copy(reqDTO, entity);
        entity.setId(prodId);
        entity.setState(reqDTO.getState());

        // 试卷模板
        Tmpl tmpl = tmplService.getById(entity.getTmplId());
        entity.setTotalScore(tmpl.getTotalScore());


        // 指定人员
        if(OpenType.PERSON.equals(reqDTO.getOpenType())){
            // 保存全部
            examPersonService.modifyAll(tempId, prodId);
        }

        // 指定部门
        if(OpenType.DEPART.equals(reqDTO.getOpenType())){
            examDepartService.saveAll(prodId, reqDTO.getDeptCodes());
        }

        // 保存考试
        this.saveOrUpdate(entity);

        // 修改错题可见状态
        AddBookDTO dto = new AddBookDTO();
        dto.setVisible(entity.getShowBook());
        dto.setRefType(BookType.EXAM);
        dto.setRefId(entity.getId());
        jobService.addCronJob(UpdateBookJob.class, null, JSON.toJSONString(dto));
    }

    @Override
    public ExamSaveReqDTO findDetail(String id) {
        ExamSaveReqDTO respDTO = new ExamSaveReqDTO();
        Exam exam = this.getById(id);
        BeanMapper.copy(exam, respDTO);

        // 考试部门
        List<String> codes = examDepartService.listByExam(id);
        respDTO.setDeptCodes(codes);

        return respDTO;
    }

    @Override
    public ExamDTO findById(String id) {
        ExamDTO respDTO = new ExamDTO();
        Exam exam = this.getById(id);
        BeanMapper.copy(exam, respDTO);
        return respDTO;
    }

    @Override
    public IPage<ExamDTO> paging(PagingReqDTO<ExamReqDTO> reqDTO) {

        // 创建分页对象
        Page query = reqDTO.toPage();

        // 查询条件
        QueryWrapper<Exam> wrapper = new QueryWrapper<>();

        // 请求参数
        ExamReqDTO params = reqDTO.getParams();

        // 权限逻辑
        String currentUserId = UserUtils.getUserId();
        // 获取用户部门
        SysUser sysUser = sysUserService.getById(currentUserId);
        String deptCode = sysUser.getDeptCode();
        // 根据部门查找考试部门映射
        List<ExamDepart> examDepartList = examDepartService.list(
                new QueryWrapper<ExamDepart>().eq("dept_code", deptCode));
        //获取课程id
        List<String> examId = examDepartList.stream()
                .map(ExamDepart::getExamId)
                .collect(Collectors.toList());
        // 管理员
        boolean isAdmin = UserUtils.getRoleLevel() > 3;
        if (isAdmin) {
            // 如果是管理员，并且自己写了查询条件，按照查询条件找
            if (params != null && !StringUtils.isBlank(params.getCreateBy())) {
                wrapper.eq("create_by", params.getCreateBy());
            }
        } else {
            // 用部门代码过滤普通用户数据
            //直接查deptCode是按照 el_course 查
            //wrapper.eq("dept_code", deptCode);
            if (examId != null && !examId.isEmpty()) {
                wrapper.in("id", examId);
            } else {
                return new Page<>();
            }
        }
        // 按更新时间倒序
        wrapper.lambda().orderByDesc(Exam::getUpdateTime);

        IPage<Exam> page = this.page(query, wrapper);

        IPage<ExamDTO> pageData = new Page<>();
        pageData.setCurrent(page.getCurrent());
        pageData.setSize(page.getSize());
        pageData.setTotal(page.getTotal());
        pageData.setRecords(
                page.getRecords().stream().map(exam -> {
                    ExamDTO dto = new ExamDTO();
                    BeanUtils.copyProperties(exam, dto);
                    return dto;
                }).collect(Collectors.toList())
        );
        //
        return pageData;

//        //请求参数
//        ExamReqDTO params = reqDTO.getParams();
//
//        //转换结果
//        IPage<ExamDTO> pageData = baseMapper.paging(reqDTO.toPage(), params);
//        return pageData;
     }

    @Override
    public IPage<ExamOnlineRespDTO> onlinePaging(PagingReqDTO<ExamReqDTO> reqDTO) {


        // 查找分页
        IPage<ExamOnlineRespDTO> pageData = baseMapper.online(reqDTO.toPage(), reqDTO.getParams());

        return pageData;
    }

    @Override
    public IPage<ExamReviewRespDTO> reviewPaging(PagingReqDTO<ExamReqDTO> reqDTO) {


        ExamReqDTO params = reqDTO.getParams();

        // 查找分页
        IPage<ExamReviewRespDTO> pageData = baseMapper.reviewPaging(reqDTO.toPage(), params);

        return pageData;
    }

    @Override
    public ExamCheckRespDTO findCheckInfo(String userId, String examId) {
        ExamCheckRespDTO respDTO = new ExamCheckRespDTO();
        Exam exam = this.getById(examId);
        BeanMapper.copy(exam, respDTO);

        // 已考试次数
        int examCount = examRecordService.countExam(userId, examId);
        respDTO.setExamCount(examCount);

        // 是否购买
        boolean isBuy = true;
        respDTO.setIsBuy(isBuy);

        // 优先校验申请
        boolean pass = examApplyService.pass(userId, examId);
        if(pass){
            // 无需人脸识别
            respDTO.setFaceOn(false);
            // 无需密码
            respDTO.setIgnorePass(true);
        }

        return respDTO;
    }

    @Override
    public int countByTmpl(String tmplId) {

        QueryWrapper<Exam> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(Exam::getTmplId, tmplId)
                .eq(Exam::getState, 0);

        return this.count(wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<String> ids) {
        for(String id: ids){
            int count = paperService.countProcess(id);
            if(count > 0){
                Exam exam = this.getById(id);
                throw new ServiceException("考试："+exam.getTitle()+"还有"+count+"个学员在考试，无法删除！");
            }
        }

        this.removeByIds(ids);
    }

    @Override
    public void markHidden(String id) {
        Exam exam = new Exam();
        exam.setId(id);
        exam.setOpenType(OpenType.HIDDEN);
        exam.setUpdateTime(new Date());
        this.updateById(exam);
    }


    @Override
    public void prepareNotify(ExamNotifyReqDTO reqDTO) {

        // 立即触发
        if("1".equalsIgnoreCase(reqDTO.getTrigger())){
            jobService.addCronJob(ExamNotifyJob.class, null, JSON.toJSONString(reqDTO));
        }else{
            jobService.addCronJob(ExamNotifyJob.class, null, CronUtils.dateToCron(reqDTO.getSendTime()), JSON.toJSONString(reqDTO));
        }
    }

    /**
     * 保存、修改考试返回考试Id
     * @param reqDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String saveById(ExamSaveReqDTO reqDTO) {
        String tempId = reqDTO.getId();
        String prodId = reqDTO.getId();

        if(tempId.startsWith("temp_")){
            prodId = IdWorker.getIdStr();
        }

        //复制参数
        Exam entity = new Exam();

        // 复制基本数据
        BeanMapper.copy(reqDTO, entity);
        entity.setId(prodId);
        entity.setState(reqDTO.getState());

        // 试卷模板
        Tmpl tmpl = tmplService.getById(entity.getTmplId());
        entity.setTotalScore(tmpl.getTotalScore());

        // 指定人员
        if(OpenType.PERSON.equals(reqDTO.getOpenType())){
            // 保存全部
            examPersonService.modifyAll(tempId, prodId);
        }

        // 指定部门
        if(OpenType.DEPART.equals(reqDTO.getOpenType())){
            examDepartService.saveAll(prodId, reqDTO.getDeptCodes());
        }

        // 保存考试
        this.saveOrUpdate(entity);

        // 修改错题可见状态
        AddBookDTO dto = new AddBookDTO();
        dto.setVisible(entity.getShowBook());
        dto.setRefType(BookType.EXAM);
        dto.setRefId(entity.getId());
        jobService.addCronJob(UpdateBookJob.class, null, JSON.toJSONString(dto));
        return prodId;
    }

    /**
     * 生成考试
     * @param openAIExamDTO
     * @return
     */
    @Override
    public String openAISaveExam(OpenAIExamDTO openAIExamDTO) {
        ExamSaveReqDTO examSaveReqDTO = new ExamSaveReqDTO();
        //无动作时间
        examSaveReqDTO.setActionInterval(300);
        //无动作检测时间
        examSaveReqDTO.setActionOn(false);
        //允许答题设备0全部 1电脑 2手机
        examSaveReqDTO.setAnswerDevice(0);
        //摄像头截屏频率
        examSaveReqDTO.setCamInterval(5);
        //摄像头开关
        examSaveReqDTO.setCamOn(false);
        //考试机会 默认三次
        examSaveReqDTO.setChance(3);
        //考试描述
        examSaveReqDTO.setContent("AI生成考试");
        String startTime=openAIExamDTO.getExamStartTime();
        String endTime=openAIExamDTO.getExamEndTime();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date startDate=null;
        Date endDate=null;
        try {
            startDate= sdf.parse(startTime);
            endDate= sdf.parse(endTime);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        //考试时间
        examSaveReqDTO.setStartTime(startDate);
        examSaveReqDTO.setEndTime(endDate);
        //部门
        examSaveReqDTO.setDeptCodes(new ArrayList<>());
        //考试类型
        examSaveReqDTO.setExamType(openAIExamDTO.getExamType());
        //人脸错误机会
        examSaveReqDTO.setFaceChance(5);
        //人脸定时检测
        examSaveReqDTO.setFaceCheck(false);
        //人脸检测开关
        examSaveReqDTO.setFaceOn(false);
        //id
        String id= UUID.randomUUID().toString().replace("-","");
        examSaveReqDTO.setId("temp_"+id);
        //离开判定秒数
        examSaveReqDTO.setLeaveCheck(5);
        //离开次数
        examSaveReqDTO.setLeaveCount(5);
        //离开检测开关
        examSaveReqDTO.setLeaveOn(false);
        //opentype 1公开2部门3定员
        examSaveReqDTO.setOpenType(1);
        //口令密码
        examSaveReqDTO.setPassword("");
        //通过积分
        examSaveReqDTO.setPoints(0);
        //价格
        examSaveReqDTO.setPrice(BigDecimal.valueOf(0));
        //及格分
        examSaveReqDTO.setQualifyScore(BigDecimal.valueOf(openAIExamDTO.getPassGrade()));
        //结果类型 ,1感谢文字,2感谢文字+成绩,3试卷明细
        examSaveReqDTO.setResultType(2);
        //是否显示错题
        examSaveReqDTO.setShowBook(false);
        //感谢词
        examSaveReqDTO.setThanks("感谢您的作答!");
        //title考试标题
        examSaveReqDTO.setTitle(openAIExamDTO.getExamTitle());
        //试卷id
        examSaveReqDTO.setTmplId(openAIExamDTO.getTemplID());
        //总分数
        examSaveReqDTO.setTotalScore(BigDecimal.valueOf(openAIExamDTO.getTotalScore()));

        examSaveReqDTO.setTotalTime(30);
        //是否开启水印
        examSaveReqDTO.setWaterMark(false);
        //生成ExamId
        String examId=saveById(examSaveReqDTO);
        return examId;
    }


}
